﻿using ClosedXML.Excel;
using DocumentFormat.OpenXml.Drawing;
using FinScreen.Model;
using QuantBox.CSharp2CTPZQ;
using SmartQuant.Data;
using SmartQuant.Indicators;
using SmartQuant.Series;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reactive.Subjects;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace FinScreen
{
    abstract class Strategy
    {
        private IDisposable _marketDisposable;
        private List<Instrument> _instruments = new List<Instrument>();

        protected IEnumerable<Instrument> Instruments { get { return _instruments; } }

        public StrategyAttribute Attribute 
        { 
            get { return GetType().GetCustomAttributes(typeof(StrategyAttribute), false).OfType<StrategyAttribute>().ElementAt(0); } 
        }
        public bool Running { get; private set; }
        protected Strategy() { }

        public void Run(IEnumerable<Instrument> instruments, StrategyContext context) 
        {
            lock (_instruments)
            {
                _instruments.Clear();
                _instruments.AddRange(instruments);

                Running = true;

                _marketDisposable = context.MarketBase.Subscribe((p1) => 
                {
                    //var ins = _instruments.SingleOrDefault((x) =>
                    //{
                    //    string id = x.Code.Split('.')[0];
                    //    return id == p1.InstrumentID;
                    //});
                    //if (ins == null) return;

                    //if (ins.Quotes.Count > 0)
                    //{
                    //    var quote = ins.Quotes.Last();

                    //    quote.Open = Math.Round(p1.OpenPrice, 2);
                    //    quote.Close = Math.Round(p1.ClosePrice, 2);
                    //    quote.PctChange = Math.Round((p1.LastPrice - p1.PreClosePrice) / p1.PreClosePrice * 100, 2);
                    //    quote.NetChange = Math.Round(p1.LastPrice - p1.PreClosePrice, 2);
                    //    quote.High = Math.Round(p1.HighestPrice, 2);
                    //    quote.Low = Math.Round(p1.LowestPrice, 2);
                    //}

                    OnMarket(p1); 
                });
                context.MarketBase.SubscribeInstruments(instruments.Select(p => p.Code).ToArray());
            }
        }
        public void Stop(StrategyContext context)
        {
            lock (_instruments)
            {
                Running = false;

                if (_marketDisposable != null) _marketDisposable.Dispose();

                context.MarketBase.UnSubscribeInstruments(_instruments.Select(p => p.Code).ToArray());

                _instruments.Clear();
            }
        }

        public abstract void OnMarket(CThostFtdcDepthMarketDataField data);
        public abstract IEnumerable<Instrument> Instrument(StrategyContext context);

        public static IEnumerable<Strategy> Discover(StrategyEnum strategyEnum)
        {
            return typeof(Strategy).Assembly.GetTypes()
                .Where(p => p.GetCustomAttributes(typeof(StrategyAttribute),false).Count()>0 && !p.IsAbstract )
                .Select(p => Activator.CreateInstance(p) as Strategy)
                .Where((p => (p.Attribute.StrategyEnum & strategyEnum) == p.Attribute.StrategyEnum));
        }

        public override string ToString()
        {
            return Attribute.Name;
        }
    }

    enum StrategyEnum
    {
        Sell = 0x01,
        Buy = 0x02,
        All = Sell | Buy
    }
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = false)]
    class StrategyAttribute : Attribute
    {
        public string Name { get; set; }
        public StrategyEnum StrategyEnum { get; set; }

        public StrategyAttribute(string name, StrategyEnum strategyEnum)
        {
            this.Name = name;
            this.StrategyEnum = strategyEnum;
        }
    }
    class StrategyContext
    {
        public MasterBase MasterBase { get; set; }
        public QuantBase QuantBase { get; set; }
        public MarketBase MarketBase { get; set; }

        public StrategyContext(MasterBase masterBase, QuantBase quantBase, MarketBase marktetBase)
        {
            MasterBase = masterBase;
            QuantBase = quantBase;
            MarketBase = marktetBase;
        }
    }
}
